
/*
 * Copyright (C) 2015-2018 Alibaba Group Holding Limited
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "common.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "esp_task_wdt.h"

#include "mqtt_api.h"
#include "dm_wrapper.h"

#include "cJSON.h"


//LED 5 12 13
//KEY 23 27 32
// #define LED_GPIO 18
// #define KEY_GPIO 19
#define KEY_PRESSED 1 //接入3V3
#define KEY_RELEASED 0 //接入GND
#define ON 0
#define OFF 1

static uint8_t LED_stat = OFF;
static uint8_t LED_laststat = OFF;
void *pclient = NULL;
static const char *TAG = "example";

const uint8_t led_gpios[] = {5, 12, 13};
const uint8_t key_gpios[] = {23, 27, 32};

const int8_t onlineStatOn = 1;
const int8_t onlineStatOff = 0;

char DEMO_PRODUCT_KEY[IOTX_PRODUCT_KEY_LEN + 1] = {0};
char DEMO_DEVICE_NAME[IOTX_DEVICE_NAME_LEN + 1] = {0};
char DEMO_DEVICE_SECRET[IOTX_DEVICE_SECRET_LEN + 1] = {0};

#define EXAMPLE_TRACE(fmt, ...)  \
    do { \
        HAL_Printf("%s|%03d :: ", __func__, __LINE__); \
        HAL_Printf(fmt, ##__VA_ARGS__); \
        HAL_Printf("%s", "\r\n"); \
    } while(0)

int light_publish(void *handle,int lightStatus);


void light_message_arrive(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg)
{
    iotx_mqtt_topic_info_t     *topic_info = (iotx_mqtt_topic_info_pt) msg->msg;


    switch (msg->event_type) {
        case IOTX_MQTT_EVENT_PUBLISH_RECEIVED:
            /* print topic name and topic message */
            EXAMPLE_TRACE("Message Arrived:");
            EXAMPLE_TRACE("Topic  : %.*s", topic_info->topic_len, topic_info->ptopic);
            EXAMPLE_TRACE("Payload: %.*s", topic_info->payload_len, topic_info->payload);
            EXAMPLE_TRACE("\n");


            const char *json_string = topic_info->payload;  
            cJSON *root = cJSON_Parse(json_string);  
        
            if (root == NULL) {  
                printf("Error before: [%s]\n", cJSON_GetErrorPtr());  
            }  

            cJSON *params_object = cJSON_GetObjectItemCaseSensitive(root, "params");  

            // 检查键 "params" 是否为JSON_Object  
            if (params_object->type != cJSON_Object) {  
                cJSON_Delete(root);  
                EXAMPLE_TRACE("Key 'params' is not an JSON Object\n");
            } 

            cJSON *status_item = cJSON_GetObjectItemCaseSensitive(params_object, "status");  

            // 检查键 "status" 是否为整型  
            if (status_item->type != cJSON_Number) {  
                EXAMPLE_TRACE("Key 'status' is not an integer\n");  
                cJSON_Delete(root);  
            } 
            else if(status_item->valueint == onlineStatOn){
                gpio_set_level(LED_GPIO, ON);
                EXAMPLE_TRACE("开灯状态已设置\n");
                light_publish(pclient,onlineStatOn);                
            } 
            else if(status_item->valueint == onlineStatOff){
                gpio_set_level(LED_GPIO, OFF);
                EXAMPLE_TRACE("关灯状态已设置\n");
                light_publish(pclient,onlineStatOff);                
            } 

            // char* myStatusOn = strstr(topic_info->payload, "\"status\":1");
            // char* myStatusOff = strstr(topic_info->payload, "\"status\":0");
            // if(myStatusOn!=NULL){
            // }
            // else if(myStatusOff!=NULL){
            // }
            break;
        default:
            break;
    }
}



int light_subscribe(void *handle)
{
    int res = 0;
    const char *fmt = "/sys/%s/%s/thing/service/property/set"; // /sys/k0yrr8izMtu/${deviceName}/thing/service/property/set
    char *topic = NULL;
    int topic_len = 0;

    topic_len = strlen(fmt) + strlen(DEMO_PRODUCT_KEY) + strlen(DEMO_DEVICE_NAME) + 1;
    topic = HAL_Malloc(topic_len);
    if (topic == NULL) {
        EXAMPLE_TRACE("memory not enough");
        return -1;
    }
    memset(topic, 0, topic_len);
    HAL_Snprintf(topic, topic_len, fmt, DEMO_PRODUCT_KEY, DEMO_DEVICE_NAME);

    res = IOT_MQTT_Subscribe(handle, topic, IOTX_MQTT_QOS0, light_message_arrive, NULL);
    if (res < 0) {
        EXAMPLE_TRACE("subscribe failed");
        HAL_Free(topic);
        return -1;
    }

    HAL_Free(topic);
    return 0;
}

// int aaa_publish(void *handle)
// {
    // int             res = 0;
    // const char     *fmt = "/%s/%s/user/get";
    // char           *topic = NULL;
    // int             topic_len = 0;
    // char           *payload = "{\"message\":\"hello!\"}";

    // topic_len = strlen(fmt) + strlen(DEMO_PRODUCT_KEY) + strlen(DEMO_DEVICE_NAME) + 1;
    // topic = HAL_Malloc(topic_len);
    // if (topic == NULL) {
        // EXAMPLE_TRACE("memory not enough");
        // return -1;
    // }
    // memset(topic, 0, topic_len);
    // HAL_Snprintf(topic, topic_len, fmt, DEMO_PRODUCT_KEY, DEMO_DEVICE_NAME);

    // res = IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payload, strlen(payload));
    // if (res < 0) {
        // EXAMPLE_TRACE("publish failed, res = %d", res);
        // HAL_Free(topic);
        // return -1;
    // }

    // HAL_Free(topic);
    // return 0;
// }

int light_publish(void *handle,int lightStatus)
{
    int             res = 0;
    const char     *fmt = "/sys/%s/%s/thing/event/property/post"; // /sys/k0yrr8izMtu/${deviceName}/thing/event/property/post
    char           *topic = NULL;
    int             topic_len = 0;
    char           *payloadOff = "{\"method\":\"thing.event.property.post\",\"id\":\"2222\",\"params\":{\"data\":\"000\",\"status\":0},\"version\":\"1.0.0\"}";
    char           *payloadOn = "{\"method\":\"thing.event.property.post\",\"id\":\"2222\",\"params\":{\"data\":\"111\",\"status\":1},\"version\":\"1.0.0\"}";

    topic_len = strlen(fmt) + strlen(DEMO_PRODUCT_KEY) + strlen(DEMO_DEVICE_NAME) + 1;
    topic = HAL_Malloc(topic_len);
    if (topic == NULL) {
        EXAMPLE_TRACE("memory not enough");
        return -1;
    }
    memset(topic, 0, topic_len);
    HAL_Snprintf(topic, topic_len, fmt, DEMO_PRODUCT_KEY, DEMO_DEVICE_NAME);

    if(lightStatus==onlineStatOff){
        res = IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payloadOff, strlen(payloadOff));
        EXAMPLE_TRACE("关灯状态已上报\n");
        // GPIO中1(ON)是开灯0(OFF)是关灯
    }
    else{
        res = IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payloadOn, strlen(payloadOn));
        EXAMPLE_TRACE("开灯状态已上报\n");
    }

    // res = lightStatus==OFF?IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payloadOff, strlen(payloadOff))
    // :IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payloadOn, strlen(payloadOn));
    if (res < 0) {
        EXAMPLE_TRACE("publish failed, res = %d", res);
        HAL_Free(topic);
        return -1;
    }

    HAL_Free(topic);
    return 0;
}

void light_event_handle(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg)
{
    EXAMPLE_TRACE("msg->event_type : %d", msg->event_type);
}

// --------------

static void send_packet()
{
    /* Publish the MQTT packet according to the LED state (ON or OFF) */
    uint8_t onlineStat = !LED_stat;
    light_publish(pclient,onlineStat);
    // IOT_MQTT_Yield(pclient, 200);
}

// static void configure_led(void)
// {
//     ESP_LOGI(TAG, "Example configured to blink GPIO LED!");
//     gpio_set_level(LED_GPIO, OFF);
//     /* Set the GPIO as a push/pull output */
//     gpio_set_direction(LED_GPIO, GPIO_MODE_OUTPUT);
// }

// static void configure_key(void)
// {
//     // ESP_LOGI(TAG, "Example configured to blink GPIO LED!");
//     // gpio_set_level(KEY_GPIO, led_state_off);
//     /* Set the GPIO as a push/pull output */
//     gpio_set_direction(KEY_GPIO, GPIO_MODE_INPUT);
// }

// static wifi_ap_record_t configure_wifi(void)  
// {  
//     struct wifi_ap_record_t ap_record;  
  
//     memset(&ap_record, 0, sizeof(struct wifi_ap_record_t));  
  
//     // 假设 ap_record.ssid 足够大，能够存储 '2-2-302' 这个SSID加上null终止符  
//     const char *ssid = "2-2-302";  
//     // 确保不会超出 ap_record.ssid 的大小  
//     HAL_Snprintf(ap_record.ssid, sizeof(ap_record.ssid), "%s", ssid);  
  
//     ap_record.primary = 1;  
  
//     return ap_record;  
// }

// static void print_log(void)
// {
//     ESP_LOGI(TAG, "LED Status is %s",LED_stat==OFF?"OFF":"ON");
// }



static void key_scan(void)
{
    uint8_t key_tmp = 0;
    // ESP_LOGI(TAG, "Example configured to blink GPIO LED!");
    key_tmp = gpio_get_level(KEY_GPIO);
    if (key_tmp == KEY_PRESSED){
        while (key_tmp == KEY_PRESSED){
            key_tmp = gpio_get_level(KEY_GPIO);
        }
        if (key_tmp == KEY_RELEASED){
            LED_laststat = LED_stat;
            LED_stat = !LED_stat;
            turn_led();
            send_packet();
            // print_log();
        }
    }
    vTaskDelay(100 / portTICK_PERIOD_MS); //按钮按下最短时间(ms)
}

// --------------

/*
 *  NOTE: About demo topic of /${productKey}/${deviceName}/user/get
 *
 *  The demo device has been configured in IoT console (https://iot.console.aliyun.com)
 *  so that its /${productKey}/${deviceName}/user/get can both be subscribed and published
 *
 *  We design this to completely demonstrate publish & subscribe process, in this way
 *  MQTT client can receive original packet sent by itself
 *
 *  For new devices created by yourself, pub/sub privilege also requires being granted
 *  to its /${productKey}/${deviceName}/user/get for successfully running whole example
 */

int mqtt_main(void *paras)
{
    int                     res = 0;
    int                     loop_cnt = 0;
    iotx_mqtt_param_t       mqtt_params;


    
    HAL_GetProductKey(DEMO_PRODUCT_KEY);
    HAL_GetDeviceName(DEMO_DEVICE_NAME);
    HAL_GetDeviceSecret(DEMO_DEVICE_SECRET);

    EXAMPLE_TRACE("mqtt example");

    /* Initialize MQTT parameter */
    /*
     * Note:
     *
     * If you did NOT set value for members of mqtt_params, SDK will use their default values
     * If you wish to customize some parameter, just un-comment value assigning expressions below
     *
     **/
    memset(&mqtt_params, 0x0, sizeof(mqtt_params));

    /**
     *
     *  MQTT connect hostname string
     *
     *  MQTT server's hostname can be customized here
     *
     *  default value is ${productKey}.iot-as-mqtt.cn-shanghai.aliyuncs.com
     */
    /* mqtt_params.t = "something.iot-as-mqtt.cn-shanghai.aliyuncs.com"; */

    /**
     *
     *  MQTT connect port number
     *
     *  TCP/TLS port which can be 443 or 1883 or 80 or etc, you can customize it here
     *
     *  default value is 1883 in TCP case, and 443 in TLS case
     */
    /* mqtt_params.port = 1883; */

    /**
     *
     * MQTT request timeout interval
     *
     * MQTT message request timeout for waiting ACK in MQTT Protocol
     *
     * default value is 2000ms.
     */
    /* mqtt_params.request_timeout_ms = 2000; */

    /**
     *
     * MQTT clean session flag
     *
     * If CleanSession is set to 0, the Server MUST resume communications with the Client based on state from
     * the current Session (as identified by the Client identifier).
     *
     * If CleanSession is set to 1, the Client and Server MUST discard any previous Session and Start a new one.
     *
     * default value is 0.
     */
    /* mqtt_params.clean_session = 0; */

    /**
     *
     * MQTT keepAlive interval
     *
     * KeepAlive is the maximum time interval that is permitted to elapse between the point at which
     * the Client finishes transmitting one Control Packet and the point it starts sending the next.
     *
     * default value is 60000.
     */
    /* mqtt_params.keepalive_interval_ms = 60000; */

    /**
     *
     * MQTT write buffer size
     *
     * Write buffer is allocated to place upstream MQTT messages, MQTT client will be limitted
     * to send packet no longer than this to Cloud
     *
     * default value is 1024.
     *
     */
    /* mqtt_params.write_buf_size = 1024; */

    /**
     *
     * MQTT read buffer size
     *
     * Write buffer is allocated to place downstream MQTT messages, MQTT client will be limitted
     * to recv packet no longer than this from Cloud
     *
     * default value is 1024.
     *
     */
    /* mqtt_params.read_buf_size = 1024; */

    /**
     *
     * MQTT event callback function
     *
     * Event callback function will be called by SDK when it want to notify user what is happening inside itself
     *
     * default value is NULL, which means PUB/SUB event won't be exposed.
     *
     */
    mqtt_params.handle_event.h_fp = light_event_handle;

    configure_led();
    configure_key();


    pclient = IOT_MQTT_Construct(&mqtt_params);
    if (NULL == pclient) {
        EXAMPLE_TRACE("MQTT construct failed");
        return -1;
    }

    res = light_subscribe(pclient);
    if (res < 0) {
        IOT_MQTT_Destroy(&pclient);
        return -1;
    }

    while (1) {
        // key_scan(); //误触发 已注释
        // esp_task_wdt_reset();
        // if (0 == loop_cnt % 300) {
        //     light_publish(pclient,OFF);
        // }
        // if (150 == loop_cnt % 300) {
        //     light_publish(pclient,ON);
        // }

        IOT_MQTT_Yield(pclient, 200);

        // loop_cnt += 1;
    }

    return 0;
}

